package co.fitstart.market.service.impl;

import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.demo2do.core.BusinessException;
import com.demo2do.core.persistence.GenericDaoSupport;
import com.demo2do.core.persistence.annotation.Dynamic;
import com.demo2do.core.utils.DateFormatUtils;
import com.demo2do.core.utils.DateUtils;

import co.fitstart.entity.coupon.Coupon;
import co.fitstart.entity.order.Order;
import co.fitstart.entity.product.Product;
import co.fitstart.entity.taste.BMIQuestion;
import co.fitstart.entity.taste.TasteSignUp;
import co.fitstart.entity.taste.TasteStat;
import co.fitstart.entity.user.User;
import co.fitstart.entity.user.UserAccount;
import co.fitstart.entity.user.WechatAccount;
import co.fitstart.entity.user.inbody.InbodyRecord;
import co.fitstart.market.Constant;
import co.fitstart.market.dao.TasteSignUpDao;
import co.fitstart.market.dao.UserDao;
import co.fitstart.market.entity.UserContext;
import co.fitstart.market.service.TasteService;

/**
 * 
 * @author haiyan_xiao
 *
 */
@Service("tasteService")
public class TasteServiceImpl implements TasteService {
    
    @Autowired
    private GenericDaoSupport genericDaoSupport;
    
    @Dynamic
    private TasteSignUpDao tasteSignUpDao;
    
    @Dynamic
    private UserDao userDao;

    /*
     * (non-Javadoc)
     * @see co.fitstart.market.service.TasteService#createBMIQuestion(co.fitstart.entity.taste.BMIQuestion)
     */
    public void createBMIQuestion(BMIQuestion bmiQuestion) {
        genericDaoSupport.save(bmiQuestion);
        
        //add stat count
        String source = bmiQuestion.getSource();
        
        if(source != null) {
            Date statDay = DateUtils.parseDate(DateFormatUtils.format(bmiQuestion.getCreateTime(), "yyyy-MM-dd"), "yyyy-MM-dd");
            
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(bmiQuestion.getCreateTime());
            Integer statHour = calendar.get(Calendar.HOUR_OF_DAY);
            
            TasteStat stat = getTasteStat(source, statDay, statHour);
            if(stat == null) {
                stat = new TasteStat(source, statDay, statHour);
                genericDaoSupport.save(stat.addQuestion());
            } else {
                genericDaoSupport.update(stat.addQuestion());
            }
        }
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.market.service.TasteService#loadBMIQuestion(java.lang.Long)
     */
    public BMIQuestion loadBMIQuestion(Long bmiQuestionId) {
        if(bmiQuestionId == null) {
            return null;
        } else {
            return genericDaoSupport.load(BMIQuestion.class, bmiQuestionId);
        }
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.market.service.TasteService#signUp(co.fitstart.entity.taste.TasteSignUp)
     */
    public void signUp(TasteSignUp tasteSignUp) {
        genericDaoSupport.save(tasteSignUp);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.market.service.TasteService#signUp(co.fitstart.entity.taste.TasteSignUp, co.fitstart.entity.taste.BMIQuestion, co.fitstart.market.entity.UserContext)
     */
    public Map<String, Object> signUp(TasteSignUp tasteSignUp, BMIQuestion bmiQuestion, UserContext userContext) {
        
        Map<String, Object> data = new HashMap<String, Object>();
        
        // Step1: if this user exsit get this user else create user
        String mobile = tasteSignUp.getMobile();
        UserAccount userAccount = this.validateUser(mobile, userContext.getOpenid());
        if (userAccount == null) {
            throw new BusinessException("用户手机账号保存失败");
        }
        User user = userAccount.getUser();
        data.put("user", user);
        
        TasteSignUp originalSignUp = this.loadTasteSignUp(user);
        if (originalSignUp != null && originalSignUp.getOrder() != null && originalSignUp.getOrder().getPaymentStatus().isUnpaid()) {// 填过问卷但未付款
            genericDaoSupport.update(originalSignUp.edit(tasteSignUp, bmiQuestion));
            data.put("order", originalSignUp.getOrder());
        } else {
            
            // Step2: create order
            // digits random number between 0 (inclusive) ~ 100 (exclusive)
            String randomNumber = String.format("%02d", Double.valueOf(Math.floor(Math.random() * 100)).intValue());
            
            // Generate code with 20 digits total
            String orderNo = new StringBuilder().append("W")
                                                .append(randomNumber)
                                                .append(DateFormatUtils.format(new Date(), "yyMMddHHmmssSSS"))
                                                .toString();
            Product product = genericDaoSupport.load(Product.class, new Long(233));//225
            Order order = new Order(orderNo, user, product, 100);
            order.addAttribute("createTime", DateFormatUtils.format(order.getCreateTime(), "yyyy-MM-dd HH:mm:SS"))
                 .addAttribute("contactPerson", tasteSignUp.getName())
                 .addAttribute("contactPhone", tasteSignUp.getMobile())
                 .addAttribute("orderPrice", 100);
            genericDaoSupport.save(order);
            
            
            // Step3: save tasteSignUp
            genericDaoSupport.save(tasteSignUp.initilization(bmiQuestion, user, order));
            
            // Step4: add stat count
            String source = tasteSignUp.getSource();
            if(source != null) {
                Date statDay = DateUtils.parseDate(DateFormatUtils.format(tasteSignUp.getCreateTime(), "yyyy-MM-dd"), "yyyy-MM-dd");
                
                Calendar calendar = Calendar.getInstance();
                calendar.setTime(tasteSignUp.getCreateTime());
                Integer statHour = calendar.get(Calendar.HOUR_OF_DAY);
                
                TasteStat stat = getTasteStat(source, statDay, statHour);
                if(stat == null) {
                    stat = new TasteStat(source, statDay, statHour);
                    genericDaoSupport.save(stat.addProfile());
                } else {
                    genericDaoSupport.update(stat.addProfile());
                }
            }
            
            if(user.getName() == null) {
                genericDaoSupport.update(user.editName(tasteSignUp.getName()));
            }
            
            data.put("order", order);
        }
        
        return data;
        
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.market.service.TasteService#loadTasteSignUp(co.fitstart.entity.user.User)
     */
    public TasteSignUp loadTasteSignUp(User user) {
        return tasteSignUpDao.loadTasteSignUp(user);
    }
    
    /**
     * 
     * @param mobile
     * @param openid
     * @return
     */
    private UserAccount validateUser(String mobile, String openid) {
        User user = userDao.loadUserByMobile(mobile);
        
        // if user is empty, create a new user
        if(user == null) {
            user = new User(mobile, false);
            genericDaoSupport.save(user);
        }
        
        // if openid exit, load userAccount by openid, bind userAccount's user and update user by wechatAccount info
        if(StringUtils.isNotEmpty(openid) && StringUtils.isNoneEmpty(openid)) {
            
            UserAccount openidUserAccount = userDao.loadUserAccount(Constant.USER_ACCOUNT_WECHAT_KEY, openid);
            if(openidUserAccount != null) {
                openidUserAccount.bind(user);
                genericDaoSupport.update(openidUserAccount);
            }
            
            WechatAccount wechatAccount = userDao.loadWechatAccount(openid);
            if(wechatAccount != null) {
                user.wechat(wechatAccount);
                genericDaoSupport.update(user);
            }
        }
        
        // if accountId is empty, create a new mobile userAccount and bind user
        UserAccount mobileUserAccount = userDao.loadUserAccount(Constant.USER_ACCOUNT_MOBILE_KEY, mobile);
        if(mobileUserAccount == null) {
            mobileUserAccount = new UserAccount(Constant.USER_ACCOUNT_MOBILE_KEY, mobile);
            genericDaoSupport.save(mobileUserAccount.bind(user));
        }else {
            genericDaoSupport.update(mobileUserAccount.bind(user));
        }
        
        // if same mobile's inbodyRecords not null, bind user inbody records
        List<InbodyRecord> inbodyRecords = this.listInbodyRecords(mobile);
        
        if(CollectionUtils.isNotEmpty(inbodyRecords)) {
            for(InbodyRecord inbodyRecord : inbodyRecords) {
                genericDaoSupport.update(inbodyRecord.bindUser(user));
            }
        }
        
        // if same mobile's coupons not null, bind user coupons
        List<Coupon> coupons = this.listCoupons(mobile, openid);
        
        if(CollectionUtils.isNotEmpty(coupons)) {
            for(Coupon coupon : coupons) {
                genericDaoSupport.update(coupon.bindUser(user.getId()));
            }
        }
        
        return mobileUserAccount;
    }
    
    /**
     * 
     * @param mobile
     * @return
     */
    @SuppressWarnings("unchecked")
    private List<InbodyRecord> listInbodyRecords(String mobile) {
        return genericDaoSupport.searchForList("FROM InbodyRecord WHERE disabled = false AND mobile = :mobile", "mobile", mobile);
    }
    
    /**
     * 
     * @param mobile
     * @return
     */
    @SuppressWarnings("unchecked")
    private List<Coupon> listCoupons(String mobile, String openid) {
        String sentence = "FROM Coupon coupon WHERE coupon.identification = :mobile";
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("mobile", mobile);
        
        if(StringUtils.isNotEmpty(openid) && StringUtils.isNoneEmpty(openid)) {
            sentence = sentence + " OR coupon.openid = :openid";
            parameters.put("openid", openid);
        }
        
        return genericDaoSupport.searchForList(sentence, parameters);
    }

    private TasteStat getTasteStat(String source, Date statDay, Integer statHour) {
        Map<String, Object> parameters = new HashMap<>();
        parameters.put("source", source);
        parameters.put("statDay", statDay);
        parameters.put("statHour", statHour);
        
        return genericDaoSupport.searchForObject("FROM TasteStat WHERE source = :source AND statDay = :statDay AND statHour = :statHour", parameters, TasteStat.class);
    }
}